Solutions to this workshop can be found here

Intro to plotting

This class will show you a tiny bit of plotting using the built-in R functions, but will pretty quickly veer into a very popular R package called ggplot2, which is often referred to as just “ggplot”. This is likely the first place in this course where you will see things that are very easy to do in R that would be much more complicated tasks (or maybe even impossible) in excel.

The basic structure of a plot

Let’s load in and consider the penguins dataset that we played with when learning about dataframes. We had loaded this in from a .csv file.

# read in the penguin data and save it to a variable called penguins
penguins <- read.csv('penguins.csv')

Let’s use base R to plot the bill length vs bill depth for all the data

plot(penguins$bill_length_mm, penguins$bill_depth_mm)

Pretty straightforward; the command is plot(x, y).

# Try making a plot of penguin flipper length vs flipper body mass
plot(penguins$flipper_length_mm, penguins$body_mass_g)

# comparing these two plots, what conclusions would you make? how could these plots be improved?

You can further modify the plot if you want to change the way the points look, etc. As I mentioned, we won’t be going deep into the details of the regular R plot function.

Intro to ggplot

Let’s try to use ggplot to plot the same data. First, install ggplot2 (if you haven’t already done so) and load it into your R session.

# uncomment the line below and install ggplot2 only if you haven't already
#install.packages('ggplot2')

# load the ggplot2 library into the current R session
library(ggplot2)

The same plot as the one we made above is actually a bit more complicated to put together in ggplot:

ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point()

The above contains the components that are the bare minimum of what we need for a ggplot plot; we can add more on later, but let’s dissect the parts of this command:

ggplot(data = <DATA>, mapping = aes(<Mapping>)) +
        <GEOM_FUNCTION>()
  • arguments:
    • data: the dataframe you want to plot
    • mapping: Any variables from your data that affect plot output, listed in aes( )
  • commands:
    • ggplot( ): required start of every ggplot command. Contains any options that we want to apply to the whole plot (which can be nothing)
    • geom_{something}( ): how you’re plotting the data. Here, we want to plot points, so we’re using geom_point; there are tons of different geoms available, one for each type of plot you might want to make.

Arguments like data and mapping can go in the parentheses after the geom, producing the same plot as above:

ggplot() +
  geom_point(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm))

But there are specific situations in which it’s better to do this (we’ll see them later)

Modifying geom properties

We can also pass additional arguments to the geom: useful ones to know are:

  • color: line color; for the default shape used in geom_point, this actually colors the inside of the shape as well
  • fill: the fill color inside a shape
  • size: point size or line thickness
  • shape: for points, this is the shape; for lines, this is the line pattern or dashyness
  • alpha: transparency level, with 0 being totally transparent and 1 being a solid, opaque color

Mapping lots of variables

The plot we made above isn’t really all that useful. It’s great to see the data across all three species on one plot, but if we’re looking at this data, we’re probably actually interested in how these species differ from each other. So how do we make ggplot visually separate the points by species?

Remember that the mapping argument deals with any properties of the plot that depend on variables in the supplied data frame. So we can modify our original code like this:

Notice that the plot above uses both a variable-dependent color (based on the penguin dataframe’s species column), which goes inside aes( ), and variable-independent values (alpha, shape, size) that applies to the whole geom_point command and goes outside aes( )

Also, notice that you got a legend for free! You didn’t have to tell ggplot how to make it, or what info to include in it; it knows automatically based on how you set up your mapping.

Depending on context, you can make color, fill, shape, size or alpha variable-dependent. Some of these (color, fill, shape) obviously make more sense for categorical variables, while others (alpha, size) make more sense for continuous variables, but ggplot will only rarely stop you from making aesthetically and data representationally questionable choices here.

Let’s try an exercise:

Stacking multiple geoms

One of the places where ggplot really shines is when you want to combine multiple data representations on one plot. For example, I really like topology-style contour plots, which ggplot can make with geom_density2d. Once we know how to make a basic plot, and combining a contour plot with a plot the individual data points is super easy in ggplot:

# note, the first two lines are just our plot from above
ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_density2d() +
  geom_point(alpha = 0.33)

Notice that the alpha argument we provided only applies to geom_point, so the contour lines don’t show any transparency. However, any arguments provided to mapping in an aes( ) statement in the ggplot( ) command apply across all geoms. (Also, notice that when we add a geom, ggplot automatically updates our legend!)

The structure of data that ggplot can plot

As you’ve seen, ggplot provides users with the power to easily change the appearance of the plot, and the statistics calculated, based on any single column in the dataframe containing the data to be plotted. But this also results in some pretty rigid rules about how your data needs to be organized. Namely, data for ggplot should be in tidy format:

Let’s take a look at what that means. Imagine the penguin data was collected for breeding pairs. Below we have created two subset of the penguins dataframe to mimic this, containing the same data on the same individuals, one male and one female per species.

penguin_pairs_1 <- penguins[c(1,2,5,6,155:158,277:280),
                            c('species', 'sex', 'bill_length_mm')]
penguin_pairs_1$breeding_pair <- rep(1:6, each = 2)

penguin_pairs_2 <-
  pivot_wider(penguin_pairs_1, names_from = 'sex',
              values_from = 'bill_length_mm', names_prefix = 'bill_length_mm_')

print(penguin_pairs_1)
print(penguin_pairs_2)

Imagine we had two graphs we wanted to make:

  1. Plot the distribution (density) of bill lengths for all the penguins, colored by species.
  2. Plot the bill length for male vs female individuals of each breeding pair, colored by species

For each of these graphs, what are the individual observations (i.e. are they breeding pairs or individual penguins)? Which is the easiest dataset to use for plotting each of these graphs with ggplot?

Try both of them out below.

The tidyr package (which, like ggplot2, is part of the tidyverse package) has some really great functions for re-organizing data, allowing you to convert from something that looks like penguin_pairs_1 into penguin_pairs_2, and vice versa. If you find yourself facing data that isn’t organized the right way for your plot, I really suggest looking over David Gresham’s tidyverse tutorial and the more up-to-date tidyr tutorial on pivot.

Why ggplot

Plot your own data!

Try plotting some of your own data! Here are some commonly used types of plots (and their corresponding geoms) for data visualization:
* scatter plots: geom_point() * density plots: geom_density() * histograms: geom_histogram() * boxplots: geom_boxplot() * barplots: geom_bar() * lineplots: geom_line()


Aside: ggplot objects

ggplot actually creates objects that we can store as variables and add onto. So, for example, we can do this:

basic_penguin_plot <-
  ggplot(data = penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point()
print(basic_penguin_plot)

# let's add another geom to this plot
penguin_plot_with_contours <-
  basic_penguin_plot + geom_density2d()
print(penguin_plot_with_contours)

themes and other options we can change

ggplot also allows a huge amount of control over other aspects of the plot (e.g. titles, axis labeling and scale, overall plot look, etc). For most of these, ggplot actually allows multiple equivalent ways to achieve the same effect.

axes + titles

Adding a title to a plot can be achieved using ggtitle()

basic_penguin_plot +
  ggtitle('Penguin Bills')

We can also modify the axis properties directly

basic_penguin_plot +
  ggtitle('Penguin Bills') +
  scale_x_continuous(name = 'Bill Length',
                     limits = c(0,60)) +
  scale_y_log10(name = 'Bill Depth',
                breaks = c(15, 18, 21))

There’s a few things going on here:

  • scale_x_continuous( ) and scale_y_log10( ): set the scale of the x and y axes. For continuous variables, they can also be plotted on a square root scale, reversed, and various other transformations. For discrete variables, use scale_x_discrete( ) and scale_y_discrete( )
  • name: the axis label
  • limits: the bounds on the axis, must be provided as a 2-number vector
  • breaks: manually assign where the tickmarks go
  • labels: for discrete variables, this can be used to rename the categories along your axis

legend

You can modify the legend in a similar way to the other mappings (e.g. the axes); for example, if we want to modify the way the thing mapped to ‘color’ on our plot is represented, we can use scale_color_discrete( ), or, if we want to manually change the values assigned to each category (e.g. the colors), scale_color_manual( ):

basic_penguin_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Penguin Species",
                     labels=c("Adelie", "Chinstrap", "Gentoo"))

We can also change the position of the legend using theme( ) (which can actually control nearly every other aesthetic aspect of the plot, such as font size, which axes get labels/tickmarks, etc).

basic_penguin_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Penguin Species",
                     labels=c("Adelie", "Chinstrap", "Gentoo")) +
  theme(legend.position = 'bottom')

themes

Finally, the overall appearance of the graph can be changed by selecting a custom ‘theme’; this is a bit confusing, since these are distinct from the theme( ) command used above.

basic_penguin_plot +
  scale_color_manual(values=c("violet", "blue", "gray"),
                     name="Penguin Species",
                     labels=c("Adelie", "Chinstrap", "Gentoo")) +
  theme(legend.position = 'bottom') +
  theme_bw()

Other cool things

More info about stacking multiple geoms

One really powerful application of this is that we can actually make each geom( ) represent a different aspect of the same data. Let’s say we’d like our datapoints to be colored by species, but we’d also like to see a contour plot of bill length vs depth across all the species. To do this, we’re going to have to move our mapping calls inside the geoms, since we now want each geom to map the data differently:

# Removed alpha for simplicity
# Made contour plot line color black (default is blue)
ggplot(data = penguins) +
  geom_density2d(mapping = aes(x = bill_length_mm, y = bill_depth_mm), color = 'black') +
  geom_point(mapping = aes(x = bill_length_mm, y = bill_depth_mm, color = species))

# can also be written as
ggplot(data = penguins, mapping = aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_density2d(color = 'black') +
  geom_point(mapping = aes(color = species))

This plot shows that mapping actually controls not just where to plot the data points and how they should look aesthetically, but also how the data is grouped when it’s represented in the plot. Notice that in the first contour plot, the statistics needed to plot the contours were computed separately for each species. However, when we removed species from the aes( ) being used by geom_density2d, the data was no longer separated by species for any of the stats calculated for this geom, and they’re instead calculated across all the points in the dataset.

Let’s try an exercise. A really useful kind of plot you can make while exploring data is a density plot, which shows pretty much a normalized, smoothed histogram of your data using geom_density. For example, if we want to get an idea of what the distribution of bill lengths in our dataset is, we can run:

# Density plot to see the distribution of Petal Lengths in our data
ggplot(data = penguins) +
  geom_density(mapping = aes(x = bill_length_mm))

Now repeat this plot, but overlaying the density plot for each species on this plot that shows the distribution across all 3 species’ data:

combining multiple data frames on one plot

ggplot makes it super easy to combine multiple datasets on one plot, assuming they have the relevant variables (dataframe columns) in common. Let’s break up the penguins dataframe to see how this works:

penguins_nonchinstrap <- subset(penguins, species != 'Chinstrap')
penguin_chinstrap_mass <- subset(penguins, species == 'Chinstrap')[, c('body_mass_g', 'species')]
print(penguins_nonchinstrap)
print(penguin_chinstrap_mass)

We now have two dataframes, containing data on different species, and with only a subset of the data in one that is contained in the other (the petal widths and species). But if petal width and species is what we want to plot, this isn’t a problem for ggplot:

ggplot() +
  geom_boxplot(data = penguins_nonchinstrap, aes(x = species, y = body_mass_g, color = species)) +
  geom_boxplot(data = penguin_chinstrap_mass, aes(x = species, y = body_mass_g, color = species))

facets

Another great tool ggplot provides is faceting. This allows you to separate data into subplots based on a column (or multiple columns):

basic_penguin_plot +
  facet_wrap( ~ species)

Notice that the x-axes are consistent among these plots.

Lots of additional packages!

Because ggplot is so popular, there’s been a ton of additional packages written that build on top of it. Here are two examples.

gganimate

Add animations to plots

# save plot with 3 frames, play at 1 frame per second
anim_save("animated_penguin_plot.gif", animated_penguin_plot, nframes = 4, fps = 1)
LS0tCnRpdGxlOiAiV29ya3Nob3AgODogUGxvdHRpbmcgKHdpdGggZ2dwbG90MikiCnN1YnRpdGxlOiB8CiAgICB8ICAgLSBwbG90dGluZyB3aXRoIGJhc2UgUgogICAgfCAgIC0gaW50cm9kdWN0aW9uIHRvIGdncGxvdAogICAgfCAgIC0gY29tYmluaW5nIG11bHRpcGxlIGRhdGEgcmVwcmVzZW50YXRpb25zIGluIGdncGxvdAogICAgfCAgIC0gY2hhbmdpbmcgdGhlIGFwcGVhcmFuY2Ugb2YgcGxvdHMKICAgIHwgICAtIG9yZ2FuaXppbmcgZGF0YSBhY2NvcmRpbmcgdG8gInRpZHkiIHByaW5jaXBsZXMKICAgIHwgICAtIGVhc3ksIHJlcHJvZHVjaWJsZSBwYXBlciBmaWd1cmVzIHdpdGggZ2dwbG90CmF1dGhvcjoKICAtIEV1Z2VuZSBQbGF2c2tpbgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGVwdGg6IDMKICAgIHRpZHk6IHllcwogICAgdG9jOiB5ZXMKLS0tCioqU29sdXRpb25zIHRvIHRoaXMgd29ya3Nob3AgY2FuIGJlIGZvdW5kIFtoZXJlXShTb2x1dGlvbnNfV29ya3Nob3BfOC5uYi5odG1sKSoqCgojIEludHJvIHRvIHBsb3R0aW5nCgpUaGlzIGNsYXNzIHdpbGwgc2hvdyB5b3UgYSAqdGlueSogYml0IG9mIHBsb3R0aW5nIHVzaW5nIHRoZSBidWlsdC1pbiBSIGZ1bmN0aW9ucywgYnV0IHdpbGwgcHJldHR5IHF1aWNrbHkgdmVlciBpbnRvIGEgdmVyeSBwb3B1bGFyIFIgcGFja2FnZSBjYWxsZWQgKipnZ3Bsb3QyKiosIHdoaWNoIGlzIG9mdGVuIHJlZmVycmVkIHRvIGFzIGp1c3QgImdncGxvdCIuIFRoaXMgaXMgbGlrZWx5IHRoZSBmaXJzdCBwbGFjZSBpbiB0aGlzIGNvdXJzZSB3aGVyZSB5b3Ugd2lsbCBzZWUgdGhpbmdzIHRoYXQgYXJlICp2ZXJ5KiBlYXN5IHRvIGRvIGluIFIgdGhhdCB3b3VsZCBiZSBtdWNoIG1vcmUgY29tcGxpY2F0ZWQgdGFza3MgKG9yIG1heWJlIGV2ZW4gaW1wb3NzaWJsZSkgaW4gZXhjZWwuCgojIyBUaGUgYmFzaWMgc3RydWN0dXJlIG9mIGEgcGxvdAoKTGV0J3MgbG9hZCBpbiBhbmQgY29uc2lkZXIgdGhlICpwZW5ndWlucyogZGF0YXNldCB0aGF0IHdlIHBsYXllZCB3aXRoIHdoZW4gbGVhcm5pbmcgYWJvdXQgZGF0YWZyYW1lcy4gV2UgaGFkIGxvYWRlZCB0aGlzIGluIGZyb20gYSAqLmNzdiogZmlsZS4KCmBgYHtyfQojIHJlYWQgaW4gdGhlIHBlbmd1aW4gZGF0YSBhbmQgc2F2ZSBpdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBwZW5ndWlucwpwZW5ndWlucyA8LSByZWFkLmNzdigncGVuZ3VpbnMuY3N2JykKYGBgCgpMZXQncyB1c2UgYmFzZSBSIHRvIHBsb3QgdGhlIGJpbGwgbGVuZ3RoIHZzIGJpbGwgZGVwdGggZm9yIGFsbCB0aGUgZGF0YQpgYGB7cn0KcGxvdChwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSwgcGVuZ3VpbnMkYmlsbF9kZXB0aF9tbSkKYGBgCgpQcmV0dHkgc3RyYWlnaHRmb3J3YXJkOyB0aGUgY29tbWFuZCBpcyBgcGxvdCh4LCB5KWAuCgpgYGB7cn0KIyBUcnkgbWFraW5nIGEgcGxvdCBvZiBwZW5ndWluIGZsaXBwZXIgbGVuZ3RoIHZzIGZsaXBwZXIgYm9keSBtYXNzCnBsb3QocGVuZ3VpbnMkZmxpcHBlcl9sZW5ndGhfbW0sIHBlbmd1aW5zJGJvZHlfbWFzc19nKQojIGNvbXBhcmluZyB0aGVzZSB0d28gcGxvdHMsIHdoYXQgY29uY2x1c2lvbnMgd291bGQgeW91IG1ha2U/IGhvdyBjb3VsZCB0aGVzZSBwbG90cyBiZSBpbXByb3ZlZD8KCmBgYAoKWW91IGNhbiBmdXJ0aGVyIG1vZGlmeSB0aGUgcGxvdCBpZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIHdheSB0aGUgcG9pbnRzIGxvb2ssIGV0Yy4gQXMgSSBtZW50aW9uZWQsIHdlIHdvbid0IGJlIGdvaW5nIGRlZXAgaW50byB0aGUgZGV0YWlscyBvZiB0aGUgcmVndWxhciBSIHBsb3QgZnVuY3Rpb24uCgojIyBJbnRybyB0byBnZ3Bsb3QKCkxldCdzIHRyeSB0byB1c2UgZ2dwbG90IHRvIHBsb3QgdGhlIHNhbWUgZGF0YS4gRmlyc3QsIGluc3RhbGwgKipnZ3Bsb3QyKiogKGlmIHlvdSBoYXZlbid0IGFscmVhZHkgZG9uZSBzbykgYW5kIGxvYWQgaXQgaW50byB5b3VyIFIgc2Vzc2lvbi4KYGBge3J9CiMgdW5jb21tZW50IHRoZSBsaW5lIGJlbG93IGFuZCBpbnN0YWxsIGdncGxvdDIgb25seSBpZiB5b3UgaGF2ZW4ndCBhbHJlYWR5CiNpbnN0YWxsLnBhY2thZ2VzKCdnZ3Bsb3QyJykKCiMgbG9hZCB0aGUgZ2dwbG90MiBsaWJyYXJ5IGludG8gdGhlIGN1cnJlbnQgUiBzZXNzaW9uCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpUaGUgc2FtZSBwbG90IGFzIHRoZSBvbmUgd2UgbWFkZSBhYm92ZSBpcyBhY3R1YWxseSBhIGJpdCBtb3JlIGNvbXBsaWNhdGVkIHRvIHB1dCB0b2dldGhlciBpbiBnZ3Bsb3Q6CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKVGhlIGFib3ZlIGNvbnRhaW5zIHRoZSBjb21wb25lbnRzIHRoYXQgYXJlIHRoZSBiYXJlIG1pbmltdW0gb2Ygd2hhdCB3ZSBuZWVkIGZvciBhIGdncGxvdCBwbG90OyB3ZSBjYW4gYWRkIG1vcmUgb24gbGF0ZXIsIGJ1dCBsZXQncyBkaXNzZWN0IHRoZSBwYXJ0cyBvZiB0aGlzIGNvbW1hbmQ6CmBgYHt9CmdncGxvdChkYXRhID0gPERBVEE+LCBtYXBwaW5nID0gYWVzKDxNYXBwaW5nPikpICsKICAgICAgICA8R0VPTV9GVU5DVElPTj4oKQpgYGAKCiogYXJndW1lbnRzOgogICAgKyAqKmRhdGEqKjogdGhlIGRhdGFmcmFtZSB5b3Ugd2FudCB0byBwbG90CiAgICArICoqbWFwcGluZyoqOiBBbnkgdmFyaWFibGVzIGZyb20geW91ciBkYXRhIHRoYXQgYWZmZWN0IHBsb3Qgb3V0cHV0LCBsaXN0ZWQgaW4gKmFlcyggKSoKKiBjb21tYW5kczoKICAgICsgKipnZ3Bsb3QoICkqKjogcmVxdWlyZWQgc3RhcnQgb2YgZXZlcnkgZ2dwbG90IGNvbW1hbmQuIENvbnRhaW5zIGFueSBvcHRpb25zIHRoYXQgd2Ugd2FudCB0byBhcHBseSB0byB0aGUgd2hvbGUgcGxvdCAod2hpY2ggY2FuIGJlIG5vdGhpbmcpCiAgICArICoqZ2VvbV97c29tZXRoaW5nfSggKSoqOiAqaG93KiB5b3UncmUgcGxvdHRpbmcgdGhlIGRhdGEuIEhlcmUsIHdlIHdhbnQgdG8gcGxvdCBwb2ludHMsIHNvIHdlJ3JlIHVzaW5nICoqZ2VvbV9wb2ludCoqOyB0aGVyZSBhcmUgKnRvbnMqIG9mIGRpZmZlcmVudCBnZW9tcyBhdmFpbGFibGUsIG9uZSBmb3IgZWFjaCB0eXBlIG9mIHBsb3QgeW91IG1pZ2h0IHdhbnQgdG8gbWFrZS4KCkFyZ3VtZW50cyBsaWtlICpkYXRhKiBhbmQgKm1hcHBpbmcqIGNhbiBnbyBpbiB0aGUgcGFyZW50aGVzZXMgYWZ0ZXIgdGhlIGdlb20sIHByb2R1Y2luZyB0aGUgc2FtZSBwbG90IGFzIGFib3ZlOgpgYGB7cn0KZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0pKQpgYGAKQnV0IHRoZXJlIGFyZSBzcGVjaWZpYyBzaXR1YXRpb25zIGluIHdoaWNoIGl0J3MgYmV0dGVyIHRvIGRvIHRoaXMgKHdlJ2xsIHNlZSB0aGVtIGxhdGVyKQoKIyMgTW9kaWZ5aW5nICoqZ2VvbSoqIHByb3BlcnRpZXMKCldlIGNhbiBhbHNvIHBhc3MgYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gdGhlIGdlb206IHVzZWZ1bCBvbmVzIHRvIGtub3cgYXJlOgoKKiBfX2NvbG9yX186IGxpbmUgY29sb3I7IGZvciB0aGUgZGVmYXVsdCBzaGFwZSB1c2VkIGluIGdlb21fcG9pbnQsIHRoaXMgYWN0dWFsbHkgY29sb3JzIHRoZSBpbnNpZGUgb2YgdGhlIHNoYXBlIGFzIHdlbGwKKiBfX2ZpbGxfXzogdGhlIGZpbGwgY29sb3IgaW5zaWRlIGEgc2hhcGUKKiBfX3NpemVfXzogcG9pbnQgc2l6ZSBvciBsaW5lIHRoaWNrbmVzcwoqIF9fc2hhcGVfXzogZm9yIHBvaW50cywgdGhpcyBpcyB0aGUgc2hhcGU7IGZvciBsaW5lcywgdGhpcyBpcyB0aGUgbGluZSBwYXR0ZXJuIG9yIGRhc2h5bmVzcwoqIF9fYWxwaGFfXzogdHJhbnNwYXJlbmN5IGxldmVsLCB3aXRoIDAgYmVpbmcgdG90YWxseSB0cmFuc3BhcmVudCBhbmQgMSBiZWluZyBhIHNvbGlkLCBvcGFxdWUgY29sb3IKCiMjIE1hcHBpbmcgbG90cyBvZiB2YXJpYWJsZXMKClRoZSBwbG90IHdlIG1hZGUgYWJvdmUgaXNuJ3QgcmVhbGx5IGFsbCB0aGF0IHVzZWZ1bC4gSXQncyBncmVhdCB0byBzZWUgdGhlIGRhdGEgYWNyb3NzIGFsbCB0aHJlZSBzcGVjaWVzIG9uIG9uZSBwbG90LCBidXQgaWYgd2UncmUgbG9va2luZyBhdCB0aGlzIGRhdGEsIHdlJ3JlIHByb2JhYmx5IGFjdHVhbGx5IGludGVyZXN0ZWQgaW4gaG93IHRoZXNlIHNwZWNpZXMgZGlmZmVyIGZyb20gZWFjaCBvdGhlci4gU28gaG93IGRvIHdlIG1ha2UgZ2dwbG90IHZpc3VhbGx5IHNlcGFyYXRlIHRoZSBwb2ludHMgYnkgc3BlY2llcz8KClJlbWVtYmVyIHRoYXQgdGhlICoqbWFwcGluZyoqIGFyZ3VtZW50IGRlYWxzIHdpdGggKmFueSBwcm9wZXJ0aWVzIG9mIHRoZSBwbG90IHRoYXQgZGVwZW5kIG9uIHZhcmlhYmxlcyBpbiB0aGUgc3VwcGxpZWQgZGF0YSBmcmFtZSouIFNvIHdlIGNhbiBtb2RpZnkgb3VyIG9yaWdpbmFsIGNvZGUgbGlrZSB0aGlzOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBwZW5ndWlucywgbWFwcGluZyA9IGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCBjb2xvciA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMzLCBzaGFwZSA9IDIzLCBzaXplID0gNSkKYGBgCgpOb3RpY2UgdGhhdCB0aGUgcGxvdCBhYm92ZSB1c2VzICpib3RoKiBhIHZhcmlhYmxlLWRlcGVuZGVudCBjb2xvciAoYmFzZWQgb24gdGhlIHBlbmd1aW4gZGF0YWZyYW1lJ3MgKnNwZWNpZXMqIGNvbHVtbiksIHdoaWNoIGdvZXMgaW5zaWRlICphZXMoICkqLCBhbmQgdmFyaWFibGUtaW5kZXBlbmRlbnQgdmFsdWVzIChhbHBoYSwgc2hhcGUsIHNpemUpIHRoYXQgYXBwbGllcyB0byB0aGUgd2hvbGUgZ2VvbV9wb2ludCBjb21tYW5kIGFuZCBnb2VzIG91dHNpZGUgKmFlcyggKSoKCkFsc28sIG5vdGljZSB0aGF0IHlvdSBnb3QgYSBsZWdlbmQgZm9yICpmcmVlKiEgWW91IGRpZG4ndCBoYXZlIHRvIHRlbGwgZ2dwbG90IGhvdyB0byBtYWtlIGl0LCBvciB3aGF0IGluZm8gdG8gaW5jbHVkZSBpbiBpdDsgaXQga25vd3MgYXV0b21hdGljYWxseSBiYXNlZCBvbiBob3cgeW91IHNldCB1cCB5b3VyICoqbWFwcGluZyoqLgoKRGVwZW5kaW5nIG9uIGNvbnRleHQsIHlvdSBjYW4gbWFrZSBjb2xvciwgZmlsbCwgc2hhcGUsIHNpemUgb3IgYWxwaGEgdmFyaWFibGUtZGVwZW5kZW50LiBTb21lIG9mIHRoZXNlIChjb2xvciwgZmlsbCwgc2hhcGUpIG9idmlvdXNseSBtYWtlIG1vcmUgc2Vuc2UgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgd2hpbGUgb3RoZXJzIChhbHBoYSwgc2l6ZSkgbWFrZSBtb3JlIHNlbnNlIGZvciBjb250aW51b3VzIHZhcmlhYmxlcywgYnV0ICpnZ3Bsb3QqIHdpbGwgb25seSByYXJlbHkgc3RvcCB5b3UgZnJvbSBtYWtpbmcgYWVzdGhldGljYWxseSBhbmQgZGF0YSByZXByZXNlbnRhdGlvbmFsbHkgcXVlc3Rpb25hYmxlIGNob2ljZXMgaGVyZS4KCkxldCdzIHRyeSBhbiBleGVyY2lzZTogCgpgYGB7cn0KIyBCYXNlZCBvbiB0aGUgY29kZSBhYm92ZSwgbWFrZSBhIHBsb3Qgd2hlcmUgYm9keSBtYXNzIGlzIG9uIHRoZSB4IGF4aXMsCiMgZmxpcHBlciBsZW5ndGggaXMgb24gdGhlIHkgYXhpcywgYWxsIHRoZSBwb2ludHMgaGF2ZSBhIHJlZCBvdXRsaW5lLCAKI3RoZSBmaWxsIG9mIHRoZSBwb2ludHMgZGVwZW5kcyBvbiB0aGUgaXNsYW5kLCAKIyBhbmQgdGhlIHNoYXBlIG9mIHRoZSBwb2ludCBkZXBlbmRzIG9uIHRoZSBzcGVjaWVzCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeD1ib2R5X21hc3NfZywgeT1mbGlwcGVyX2xlbmd0aF9tbSwgY29sb3IgPSBpc2xhbmQsIHNoYXBlID0gc3BlY2llcykpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKQpgYGAKCgojIyBTdGFja2luZyBtdWx0aXBsZSAqKmdlb21zKioKCk9uZSBvZiB0aGUgcGxhY2VzIHdoZXJlIGdncGxvdCByZWFsbHkgc2hpbmVzIGlzIHdoZW4geW91IHdhbnQgdG8gY29tYmluZSBtdWx0aXBsZSBkYXRhIHJlcHJlc2VudGF0aW9ucyBvbiBvbmUgcGxvdC4gRm9yIGV4YW1wbGUsIEkgKnJlYWxseSogbGlrZSB0b3BvbG9neS1zdHlsZSBjb250b3VyIHBsb3RzLCB3aGljaCBnZ3Bsb3QgY2FuIG1ha2Ugd2l0aCAqKmdlb21fZGVuc2l0eTJkKiouIE9uY2Ugd2Uga25vdyBob3cgdG8gbWFrZSBhIGJhc2ljIHBsb3QsIGFuZCBjb21iaW5pbmcgYSBjb250b3VyIHBsb3Qgd2l0aCBhIHBsb3QgdGhlIGluZGl2aWR1YWwgZGF0YSBwb2ludHMgaXMgc3VwZXIgZWFzeSBpbiBnZ3Bsb3Q6CmBgYHtyfQojIG5vdGUsIHRoZSBmaXJzdCB0d28gbGluZXMgYXJlIGp1c3Qgb3VyIHBsb3QgZnJvbSBhYm92ZQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJpbGxfZGVwdGhfbW0sIGNvbG9yID0gc3BlY2llcykpICsKICBnZW9tX2RlbnNpdHkyZCgpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4zMykKYGBgCk5vdGljZSB0aGF0IHRoZSAqYWxwaGEqIGFyZ3VtZW50IHdlIHByb3ZpZGVkIG9ubHkgYXBwbGllcyB0byBnZW9tX3BvaW50LCBzbyB0aGUgY29udG91ciBsaW5lcyBkb24ndCBzaG93IGFueSB0cmFuc3BhcmVuY3kuIEhvd2V2ZXIsIGFueSBhcmd1bWVudHMgcHJvdmlkZWQgdG8gKiptYXBwaW5nKiogaW4gYW4gYWVzKCApIHN0YXRlbWVudCBpbiB0aGUgKipnZ3Bsb3QoICkqKiBjb21tYW5kIGFwcGx5IGFjcm9zcyBhbGwgZ2VvbXMuIChBbHNvLCBub3RpY2UgdGhhdCB3aGVuIHdlIGFkZCBhIGdlb20sIGdncGxvdCBhdXRvbWF0aWNhbGx5IHVwZGF0ZXMgb3VyIGxlZ2VuZCEpCgoKIyBUaGUgc3RydWN0dXJlIG9mIGRhdGEgdGhhdCBnZ3Bsb3QgY2FuIHBsb3QKCkFzIHlvdSd2ZSBzZWVuLCBnZ3Bsb3QgcHJvdmlkZXMgdXNlcnMgd2l0aCB0aGUgcG93ZXIgdG8gZWFzaWx5IGNoYW5nZSB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgcGxvdCwgYW5kIHRoZSBzdGF0aXN0aWNzIGNhbGN1bGF0ZWQsIGJhc2VkIG9uIGFueSBzaW5nbGUgY29sdW1uIGluIHRoZSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgZGF0YSB0byBiZSBwbG90dGVkLiBCdXQgdGhpcyBhbHNvIHJlc3VsdHMgaW4gc29tZSBwcmV0dHkgcmlnaWQgcnVsZXMgYWJvdXQgaG93IHlvdXIgZGF0YSBuZWVkcyB0byBiZSBvcmdhbml6ZWQuIE5hbWVseSwgZGF0YSBmb3IgZ2dwbG90IHNob3VsZCBiZSBpbiBbdGlkeSBmb3JtYXRdKGh0dHBzOi8vbGVhcm4uZ2VuY29yZS5iaW8ubnl1LmVkdS90aWR5dmVyc2UvKToKCiogZWFjaCB2YXJpYWJsZSBtdXN0IGhhdmUgaXRzIG93biBjb2x1bW4KKiBlYWNoIG9ic2VydmF0aW9uIG11c3QgaGF2ZSBpdHMgb3duIHJvdyAoYnV0IHdoYXQncyBhbiBvYnNlcnZhdGlvbj8pCiogZWFjaCB2YWx1ZSBtdXN0IGhhdmUgaXRzIG93biBjZWxsCgpMZXQncyB0YWtlIGEgbG9vayBhdCB3aGF0IHRoYXQgbWVhbnMuIEltYWdpbmUgdGhlIHBlbmd1aW4gZGF0YSB3YXMgY29sbGVjdGVkIGZvciBicmVlZGluZyBwYWlycy4gQmVsb3cgd2UgaGF2ZSBjcmVhdGVkIHR3byBzdWJzZXQgb2YgdGhlICpwZW5ndWlucyogZGF0YWZyYW1lIHRvIG1pbWljIHRoaXMsIGNvbnRhaW5pbmcgdGhlIHNhbWUgZGF0YSBvbiB0aGUgc2FtZSBpbmRpdmlkdWFscywgb25lIG1hbGUgYW5kIG9uZSBmZW1hbGUgcGVyIHNwZWNpZXMuCmBgYHtyfQpwZW5ndWluX3BhaXJzXzEgPC0gcGVuZ3VpbnNbYygxLDIsNSw2LDE1NToxNTgsMjc3OjI4MCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCdzcGVjaWVzJywgJ3NleCcsICdiaWxsX2xlbmd0aF9tbScpXQpwZW5ndWluX3BhaXJzXzEkYnJlZWRpbmdfcGFpciA8LSByZXAoMTo2LCBlYWNoID0gMikKCnBlbmd1aW5fcGFpcnNfMiA8LQogIHBpdm90X3dpZGVyKHBlbmd1aW5fcGFpcnNfMSwgbmFtZXNfZnJvbSA9ICdzZXgnLAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gJ2JpbGxfbGVuZ3RoX21tJywgbmFtZXNfcHJlZml4ID0gJ2JpbGxfbGVuZ3RoX21tXycpCgpwcmludChwZW5ndWluX3BhaXJzXzEpCnByaW50KHBlbmd1aW5fcGFpcnNfMikKYGBgCgpJbWFnaW5lIHdlIGhhZCB0d28gZ3JhcGhzIHdlIHdhbnRlZCB0byBtYWtlOgoKMS4gUGxvdCB0aGUgZGlzdHJpYnV0aW9uIChkZW5zaXR5KSBvZiBiaWxsIGxlbmd0aHMgZm9yIGFsbCB0aGUgcGVuZ3VpbnMsIGNvbG9yZWQgYnkgc3BlY2llcy4KMi4gUGxvdCB0aGUgYmlsbCBsZW5ndGggZm9yIG1hbGUgdnMgZmVtYWxlIGluZGl2aWR1YWxzIG9mIGVhY2ggYnJlZWRpbmcgcGFpciwgY29sb3JlZCBieSBzcGVjaWVzCgpGb3IgZWFjaCBvZiB0aGVzZSBncmFwaHMsIHdoYXQgYXJlIHRoZSAqaW5kaXZpZHVhbCBvYnNlcnZhdGlvbnMqIChpLmUuIGFyZSB0aGV5IGJyZWVkaW5nIHBhaXJzIG9yIGluZGl2aWR1YWwgcGVuZ3VpbnMpPyBXaGljaCBpcyB0aGUgZWFzaWVzdCBkYXRhc2V0IHRvIHVzZSBmb3IgcGxvdHRpbmcgZWFjaCBvZiB0aGVzZSBncmFwaHMgd2l0aCAqKmdncGxvdCoqPwoKVHJ5IGJvdGggb2YgdGhlbSBvdXQgYmVsb3cuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBwZW5ndWluX3BhaXJzXzEsIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCBjb2xvciA9IHNwZWNpZXMpKSArCiAgZ2VvbV9kZW5zaXR5KCkKZ2dwbG90KGRhdGEgPSBwZW5ndWluX3BhaXJzXzIsIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tX21hbGUsIHkgPSBiaWxsX2xlbmd0aF9tbV9mZW1hbGUsIGNvbG9yID0gc3BlY2llcykpICsgCiAgZ2VvbV9wb2ludCgpCmBgYAoKVGhlICoqdGlkeXIqKiBwYWNrYWdlICh3aGljaCwgbGlrZSAqKmdncGxvdDIqKiwgaXMgcGFydCBvZiB0aGUgKip0aWR5dmVyc2UqKiBwYWNrYWdlKSBoYXMgc29tZSByZWFsbHkgZ3JlYXQgZnVuY3Rpb25zIGZvciByZS1vcmdhbml6aW5nIGRhdGEsIGFsbG93aW5nIHlvdSB0byBjb252ZXJ0IGZyb20gc29tZXRoaW5nIHRoYXQgbG9va3MgbGlrZSBgcGVuZ3Vpbl9wYWlyc18xYCBpbnRvIGBwZW5ndWluX3BhaXJzXzJgLCBhbmQgdmljZSB2ZXJzYS4gSWYgeW91IGZpbmQgeW91cnNlbGYgZmFjaW5nIGRhdGEgdGhhdCBpc24ndCBvcmdhbml6ZWQgdGhlIHJpZ2h0IHdheSBmb3IgeW91ciBwbG90LCBJIHJlYWxseSBzdWdnZXN0IGxvb2tpbmcgb3ZlciBbRGF2aWQgR3Jlc2hhbSdzIHRpZHl2ZXJzZSB0dXRvcmlhbF0oaHR0cHM6Ly9sZWFybi5nZW5jb3JlLmJpby5ueXUuZWR1L3RpZHl2ZXJzZS8pIGFuZCB0aGUgbW9yZSB1cC10by1kYXRlIFt0aWR5ciB0dXRvcmlhbCBvbiBwaXZvdF0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3Bpdm90Lmh0bWwpLgoKIyBXaHkgZ2dwbG90CgoqIF9fZWFzeSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzX186IHNlcGFyYXRpb24gb2YgdmFyaWFibGUgbWFwcGluZyBmcm9tIHZpc3VhbCByZXByZXNlbnRhdGlvbiAoZS5nLiBnZW9tcykgbWFrZXMgaXQgZWFzeSB0byB0cnkgZGlmZmVyZW50IHdheXMgb2YgcGxvdHRpbmcgZGF0YQoqIF9fYXV0b21hdGlvbiBvZiB0aGUgYm9yaW5nIHN0dWZmX186IGdlbmVyYXRpb24gb2YgbGVnZW5kcywgYXhpcyBib3VuZHMsIGV0YyBpcyBkb25lIHZlcnkgd2VsbCBhdXRvbWF0aWNhbGx5IGJhc2VkIG9uIHRoZSBkYXRhIGFuZCB2YXJpYWJsZXMgeW91J3JlIHBsb3R0aW5nIChidXQgeW91IGNhbiBoYXZlIGZpbmVyIGNvbnRyb2wgb2YgaXQgaWYgeW91J2QgbGlrZSkKKiBfX2F1dG9tYXRlZCBzdGF0c19fOiBsb3RzIG9mIGdlb21zIHRoYXQgY2FsY3VsYXRlIGFuZCBkaXNwbGF5IHN0YXRpc3RpY3Mgb2YgeW91ciBkYXRhLiBUaGVzZSBzdGF0aXN0aWNzIGFyZSBhdXRvbWF0aWNhbGx5IGNhbGN1bGF0ZWQgYmFzZWQgb24geW91ciBncm91cGluZyBvZiB0aGUgZGF0YSwgaG93IHlvdSBzcGVjaWZ5IHlvdXIgYXhlcyAoZS5nLiBsaW5lYXIgdnMgbG9nIHNjYWxlKSwgZXRjLgogICAgKyAqZ2VvbV9kZW5zaXR5KiBhbmQgKmdlb21fZGVuc2l0eTJkKiBmb3IgZGVuc2l0eSBlc3RpbWF0aW9uCiAgICArICpnZW9tX3Ntb290aCogZm9yIHRyZW5kbGluZXMgd2l0aCBlcnJvciByaWJib25zCiAgICArIFtzdGF0X3N1bW1hcnldKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdGF0X3N1bW1hcnkuaHRtbCkgZm9yIG90aGVyIHdheXMgdG8gYmluIGFuZCBzdW1tYXJpemUgZGF0YSkKKiBfX3Bsb3RzIGFzIG9iamVjdHNfXzogc3RvcmluZyBwbG90cyBhcyBvYmplY3RzIGFsbG93cyB0aGVtIHRvIGJlIGVhc2lseSBtb2RpZmllZCBhbmQgY29tYmluZWQgaW50byBmaWd1cmVzCiogX19pbmNyZWRpYmx5IGhlbHBmdWwgb25saW5lIGV4YW1wbGVzX186IHRoZSAqdGlkeXZlcnNlKiB3ZWJzaXRlIGNvbnRhaW5zIGFuIGluY3JlZGlibGUgb25saW5lIG1hbnVhbCB3aXRoIGV4cGxhbmF0aW9ucyBhbmQgY2xlYXIgZXhhbXBsZXMgZm9yIG5lYXJseSBldmVyeXRoaW5nIHlvdSBtaWdodCB3YW50IHRvIGRvIGluIGdncGxvdDogW2h0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9dKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8pCgojIFBsb3QgeW91ciBvd24gZGF0YSEKVHJ5IHBsb3R0aW5nIHNvbWUgb2YgeW91ciBvd24gZGF0YSEgSGVyZSBhcmUgc29tZSBjb21tb25seSB1c2VkIHR5cGVzIG9mIHBsb3RzIChhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBnZW9tcykgZm9yIGRhdGEgdmlzdWFsaXphdGlvbjogIAoqIHNjYXR0ZXIgcGxvdHM6IGdlb21fcG9pbnQoKQoqIGRlbnNpdHkgcGxvdHM6IGdlb21fZGVuc2l0eSgpCiogaGlzdG9ncmFtczogZ2VvbV9oaXN0b2dyYW0oKQoqIGJveHBsb3RzOiBnZW9tX2JveHBsb3QoKQoqIGJhcnBsb3RzOiBnZW9tX2JhcigpCiogbGluZXBsb3RzOiBnZW9tX2xpbmUoKQoKKioqCgojIyBBc2lkZTogZ2dwbG90IG9iamVjdHMKCmdncGxvdCBhY3R1YWxseSBjcmVhdGVzIG9iamVjdHMgdGhhdCB3ZSBjYW4gc3RvcmUgYXMgdmFyaWFibGVzIGFuZCBhZGQgb250by4gU28sIGZvciBleGFtcGxlLCB3ZSBjYW4gZG8gdGhpczoKYGBge3J9CmJhc2ljX3Blbmd1aW5fcGxvdCA8LQogIGdncGxvdChkYXRhID0gcGVuZ3VpbnMsIGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCBjb2xvciA9IHNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludCgpCnByaW50KGJhc2ljX3Blbmd1aW5fcGxvdCkKIyBsZXQncyBhZGQgYW5vdGhlciBnZW9tIHRvIHRoaXMgcGxvdApwZW5ndWluX3Bsb3Rfd2l0aF9jb250b3VycyA8LQogIGJhc2ljX3Blbmd1aW5fcGxvdCArIGdlb21fZGVuc2l0eTJkKCkKcHJpbnQocGVuZ3Vpbl9wbG90X3dpdGhfY29udG91cnMpCmBgYAoKCiMjIHRoZW1lcyBhbmQgb3RoZXIgb3B0aW9ucyB3ZSBjYW4gY2hhbmdlCgpnZ3Bsb3QgYWxzbyBhbGxvd3MgYSBodWdlIGFtb3VudCBvZiBjb250cm9sIG92ZXIgb3RoZXIgYXNwZWN0cyBvZiB0aGUgcGxvdCAoZS5nLiB0aXRsZXMsIGF4aXMgbGFiZWxpbmcgYW5kIHNjYWxlLCBvdmVyYWxsIHBsb3QgbG9vaywgZXRjKS4gRm9yIG1vc3Qgb2YgdGhlc2UsIGdncGxvdCBhY3R1YWxseSBhbGxvd3MgbXVsdGlwbGUgZXF1aXZhbGVudCB3YXlzIHRvIGFjaGlldmUgdGhlIHNhbWUgZWZmZWN0LgoKIyMjIGF4ZXMgKyB0aXRsZXMKCkFkZGluZyBhIHRpdGxlIHRvIGEgcGxvdCBjYW4gYmUgYWNoaWV2ZWQgdXNpbmcgZ2d0aXRsZSgpCmBgYHtyfQpiYXNpY19wZW5ndWluX3Bsb3QgKwogIGdndGl0bGUoJ1Blbmd1aW4gQmlsbHMnKQpgYGAKCldlIGNhbiBhbHNvIG1vZGlmeSB0aGUgYXhpcyBwcm9wZXJ0aWVzIGRpcmVjdGx5CmBgYHtyfQpiYXNpY19wZW5ndWluX3Bsb3QgKwogIGdndGl0bGUoJ1Blbmd1aW4gQmlsbHMnKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAnQmlsbCBMZW5ndGgnLAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsNjApKSArCiAgc2NhbGVfeV9sb2cxMChuYW1lID0gJ0JpbGwgRGVwdGgnLAogICAgICAgICAgICAgICAgYnJlYWtzID0gYygxNSwgMTgsIDIxKSkKYGBgClRoZXJlJ3MgYSBmZXcgdGhpbmdzIGdvaW5nIG9uIGhlcmU6CgoqIF9fc2NhbGVfeF9jb250aW51b3VzKCApX18gYW5kIF9fc2NhbGVfeV9sb2cxMCggKV9fOiBzZXQgdGhlIHNjYWxlIG9mIHRoZSB4IGFuZCB5IGF4ZXMuIEZvciBjb250aW51b3VzIHZhcmlhYmxlcywgdGhleSBjYW4gYWxzbyBiZSBwbG90dGVkIG9uIGEgc3F1YXJlIHJvb3Qgc2NhbGUsIHJldmVyc2VkLCBhbmQgdmFyaW91cyBvdGhlciB0cmFuc2Zvcm1hdGlvbnMuIEZvciBkaXNjcmV0ZSB2YXJpYWJsZXMsIHVzZSAqKnNjYWxlX3hfZGlzY3JldGUoICkqKiBhbmQgKipzY2FsZV95X2Rpc2NyZXRlKCApKioKKiBfX25hbWVfXzogdGhlIGF4aXMgbGFiZWwKKiBfX2xpbWl0c19fOiB0aGUgYm91bmRzIG9uIHRoZSBheGlzLCBtdXN0IGJlIHByb3ZpZGVkIGFzIGEgMi1udW1iZXIgdmVjdG9yCiogX19icmVha3NfXzogbWFudWFsbHkgYXNzaWduIHdoZXJlIHRoZSB0aWNrbWFya3MgZ28KKiBfX2xhYmVsc19fOiBmb3IgZGlzY3JldGUgdmFyaWFibGVzLCB0aGlzIGNhbiBiZSB1c2VkIHRvIHJlbmFtZSB0aGUgY2F0ZWdvcmllcyBhbG9uZyB5b3VyIGF4aXMKCiMjIyBsZWdlbmQKCllvdSBjYW4gbW9kaWZ5IHRoZSBsZWdlbmQgaW4gYSBzaW1pbGFyIHdheSB0byB0aGUgb3RoZXIgbWFwcGluZ3MgKGUuZy4gdGhlIGF4ZXMpOyBmb3IgZXhhbXBsZSwgaWYgd2Ugd2FudCB0byBtb2RpZnkgdGhlIHdheSB0aGUgdGhpbmcgbWFwcGVkIHRvICdjb2xvcicgb24gb3VyIHBsb3QgaXMgcmVwcmVzZW50ZWQsIHdlIGNhbiB1c2UgKipzY2FsZV9jb2xvcl9kaXNjcmV0ZSggKSoqLCBvciwgaWYgd2Ugd2FudCB0byBtYW51YWxseSBjaGFuZ2UgdGhlIHZhbHVlcyBhc3NpZ25lZCB0byBlYWNoIGNhdGVnb3J5IChlLmcuIHRoZSBjb2xvcnMpLCAqKnNjYWxlX2NvbG9yX21hbnVhbCggKSoqOgpgYGB7cn0KYmFzaWNfcGVuZ3Vpbl9wbG90ICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInZpb2xldCIsICJibHVlIiwgImdyYXkiKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUGVuZ3VpbiBTcGVjaWVzIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkFkZWxpZSIsICJDaGluc3RyYXAiLCAiR2VudG9vIikpCmBgYApXZSBjYW4gYWxzbyBjaGFuZ2UgdGhlIHBvc2l0aW9uIG9mIHRoZSBsZWdlbmQgdXNpbmcgKip0aGVtZSggKSoqICh3aGljaCBjYW4gYWN0dWFsbHkgY29udHJvbCBuZWFybHkgZXZlcnkgb3RoZXIgYWVzdGhldGljIGFzcGVjdCBvZiB0aGUgcGxvdCwgc3VjaCBhcyBmb250IHNpemUsIHdoaWNoIGF4ZXMgZ2V0IGxhYmVscy90aWNrbWFya3MsIGV0YykuCgpgYGB7cn0KYmFzaWNfcGVuZ3Vpbl9wbG90ICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInZpb2xldCIsICJibHVlIiwgImdyYXkiKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUGVuZ3VpbiBTcGVjaWVzIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkFkZWxpZSIsICJDaGluc3RyYXAiLCAiR2VudG9vIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykKYGBgCgojIyMgdGhlbWVzCgpGaW5hbGx5LCB0aGUgb3ZlcmFsbCBhcHBlYXJhbmNlIG9mIHRoZSBncmFwaCBjYW4gYmUgY2hhbmdlZCBieSBzZWxlY3RpbmcgYSBjdXN0b20gJ3RoZW1lJzsgdGhpcyBpcyBhIGJpdCBjb25mdXNpbmcsIHNpbmNlIHRoZXNlIGFyZSBkaXN0aW5jdCBmcm9tIHRoZSAqKnRoZW1lKCApKiogY29tbWFuZCB1c2VkIGFib3ZlLgpgYGB7cn0KYmFzaWNfcGVuZ3Vpbl9wbG90ICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoInZpb2xldCIsICJibHVlIiwgImdyYXkiKSwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUGVuZ3VpbiBTcGVjaWVzIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkFkZWxpZSIsICJDaGluc3RyYXAiLCAiR2VudG9vIikpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKwogIHRoZW1lX2J3KCkKYGBgCgoKIyBPdGhlciBjb29sIHRoaW5ncwoKIyMgTW9yZSBpbmZvIGFib3V0IHN0YWNraW5nIG11bHRpcGxlIGdlb21zCgpPbmUgcmVhbGx5IHBvd2VyZnVsIGFwcGxpY2F0aW9uIG9mIHRoaXMgaXMgdGhhdCB3ZSBjYW4gYWN0dWFsbHkgbWFrZSBlYWNoIGdlb20oICkgcmVwcmVzZW50IGEgZGlmZmVyZW50IGFzcGVjdCBvZiB0aGUgc2FtZSBkYXRhLiBMZXQncyBzYXkgd2UnZCBsaWtlIG91ciBkYXRhcG9pbnRzIHRvIGJlIGNvbG9yZWQgYnkgc3BlY2llcywgYnV0IHdlJ2QgYWxzbyBsaWtlIHRvIHNlZSBhIGNvbnRvdXIgcGxvdCBvZiBiaWxsIGxlbmd0aCB2cyBkZXB0aCAqYWNyb3NzIGFsbCB0aGUgc3BlY2llcyouIFRvIGRvIHRoaXMsIHdlJ3JlIGdvaW5nIHRvIGhhdmUgdG8gbW92ZSBvdXIgKiptYXBwaW5nKiogY2FsbHMgaW5zaWRlIHRoZSBnZW9tcywgc2luY2Ugd2Ugbm93IHdhbnQgZWFjaCBnZW9tIHRvIG1hcCB0aGUgZGF0YSBkaWZmZXJlbnRseToKYGBge3J9CiMgUmVtb3ZlZCBhbHBoYSBmb3Igc2ltcGxpY2l0eQojIE1hZGUgY29udG91ciBwbG90IGxpbmUgY29sb3IgYmxhY2sgKGRlZmF1bHQgaXMgYmx1ZSkKZ2dwbG90KGRhdGEgPSBwZW5ndWlucykgKwogIGdlb21fZGVuc2l0eTJkKG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSksIGNvbG9yID0gJ2JsYWNrJykgKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCBjb2xvciA9IHNwZWNpZXMpKQpgYGAKCmBgYHtyIGV2YWw9Rn0KIyBjYW4gYWxzbyBiZSB3cml0dGVuIGFzCmdncGxvdChkYXRhID0gcGVuZ3VpbnMsIG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCB5ID0gYmlsbF9kZXB0aF9tbSkpICsKICBnZW9tX2RlbnNpdHkyZChjb2xvciA9ICdibGFjaycpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBzcGVjaWVzKSkKYGBgClRoaXMgcGxvdCBzaG93cyB0aGF0ICoqbWFwcGluZyoqIGFjdHVhbGx5IGNvbnRyb2xzIG5vdCBqdXN0IHdoZXJlIHRvIHBsb3QgdGhlIGRhdGEgcG9pbnRzIGFuZCBob3cgdGhleSBzaG91bGQgbG9vayBhZXN0aGV0aWNhbGx5LCBidXQgYWxzbyBob3cgdGhlIGRhdGEgaXMgZ3JvdXBlZCB3aGVuIGl0J3MgcmVwcmVzZW50ZWQgaW4gdGhlIHBsb3QuIE5vdGljZSB0aGF0IGluIHRoZSBmaXJzdCBjb250b3VyIHBsb3QsIHRoZSBzdGF0aXN0aWNzIG5lZWRlZCB0byBwbG90IHRoZSBjb250b3VycyB3ZXJlIGNvbXB1dGVkIHNlcGFyYXRlbHkgZm9yIGVhY2ggc3BlY2llcy4gSG93ZXZlciwgd2hlbiB3ZSByZW1vdmVkIHNwZWNpZXMgZnJvbSB0aGUgYWVzKCApIGJlaW5nIHVzZWQgYnkgZ2VvbV9kZW5zaXR5MmQsIHRoZSBkYXRhIHdhcyBubyBsb25nZXIgc2VwYXJhdGVkIGJ5IHNwZWNpZXMgZm9yIGFueSBvZiB0aGUgc3RhdHMgY2FsY3VsYXRlZCBmb3IgdGhpcyBnZW9tLCBhbmQgdGhleSdyZSBpbnN0ZWFkIGNhbGN1bGF0ZWQgYWNyb3NzIGFsbCB0aGUgcG9pbnRzIGluIHRoZSBkYXRhc2V0LgoKTGV0J3MgdHJ5IGFuIGV4ZXJjaXNlLiBBIHJlYWxseSB1c2VmdWwga2luZCBvZiBwbG90IHlvdSBjYW4gbWFrZSB3aGlsZSBleHBsb3JpbmcgZGF0YSBpcyBhICpkZW5zaXR5KiBwbG90LCB3aGljaCBzaG93cyBwcmV0dHkgbXVjaCBhIG5vcm1hbGl6ZWQsIHNtb290aGVkIGhpc3RvZ3JhbSBvZiB5b3VyIGRhdGEgdXNpbmcgKipnZW9tX2RlbnNpdHkqKi4gRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnQgdG8gZ2V0IGFuIGlkZWEgb2Ygd2hhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGJpbGwgbGVuZ3RocyBpbiBvdXIgZGF0YXNldCBpcywgd2UgY2FuIHJ1bjoKYGBge3J9CiMgRGVuc2l0eSBwbG90IHRvIHNlZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGJpbGwgbGVuZ3RocyBpbiBvdXIgZGF0YQpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zKSArCiAgZ2VvbV9kZW5zaXR5KG1hcHBpbmcgPSBhZXMoeCA9IGJpbGxfbGVuZ3RoX21tKSkKYGBgCgpOb3cgcmVwZWF0IHRoaXMgcGxvdCwgYnV0IG92ZXJsYXlpbmcgdGhlIGRlbnNpdHkgcGxvdCBmb3IgZWFjaCBzcGVjaWVzIG9uIHRoaXMgcGxvdCB0aGF0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gYWNyb3NzIGFsbCAzIHNwZWNpZXMnIGRhdGE6CmBgYHtyfQojIE1ha2UgYSBkZW5zaXR5IHBsb3QgdGhhdCBzaG93cyBib3RoIHRoZSBkaXN0cmlidXRpb24gb2YgYmlsbF9sZW5ndGhfbW0gaW4gYWxsCiMgdGhlIGRhdGEgdG9nZXRoZXIgaW4gb25lIGNvbG9yLCBhbmQgdGhlIGRpc3RyaWJ1dGlvbiBmb3IgZWFjaCBzcGVjaWVzJwojIGJpbGxfbGVuZ2hfbW0gZWFjaCBpbiBpdHMgb3duIGNvbG9yCgpnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhjb2xvciA9IHNwZWNpZXMpKQoKIyBCb251czogQ2hhbmdlIHRoZSBsaW5ldHlwZSBvZiB0aGUgc3BlY2llcycgZGVuc2l0eSBwbG90cyBzbyB0aGF0IGVhY2ggc3BlY2llcwojIGhhcyB0aGUgc2FtZSBkYXNoZWQgbGluZSwgYnV0IHRoZSBsaW5lIHJlcHJlc2VudGluZyByZXN1bHRzIGFjcm9zcyBhbGwgdGhlCiMgZGF0YSBpcyBzb2xpZApnZ3Bsb3QoZGF0YSA9IHBlbmd1aW5zLCBtYXBwaW5nID0gYWVzKHggPSBiaWxsX2xlbmd0aF9tbSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhjb2xvciA9IHNwZWNpZXMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKQpgYGAKCiMjIGNvbWJpbmluZyBtdWx0aXBsZSBkYXRhIGZyYW1lcyBvbiBvbmUgcGxvdAoKZ2dwbG90IG1ha2VzIGl0IHN1cGVyIGVhc3kgdG8gY29tYmluZSBtdWx0aXBsZSBkYXRhc2V0cyBvbiBvbmUgcGxvdCwgYXNzdW1pbmcgdGhleSBoYXZlIHRoZSByZWxldmFudCB2YXJpYWJsZXMgKGRhdGFmcmFtZSBjb2x1bW5zKSBpbiBjb21tb24uIExldCdzIGJyZWFrIHVwIHRoZSBwZW5ndWlucyBkYXRhZnJhbWUgdG8gc2VlIGhvdyB0aGlzIHdvcmtzOgpgYGB7cn0KcGVuZ3VpbnNfbm9uY2hpbnN0cmFwIDwtIHN1YnNldChwZW5ndWlucywgc3BlY2llcyAhPSAnQ2hpbnN0cmFwJykKcGVuZ3Vpbl9jaGluc3RyYXBfbWFzcyA8LSBzdWJzZXQocGVuZ3VpbnMsIHNwZWNpZXMgPT0gJ0NoaW5zdHJhcCcpWywgYygnYm9keV9tYXNzX2cnLCAnc3BlY2llcycpXQpwcmludChwZW5ndWluc19ub25jaGluc3RyYXApCnByaW50KHBlbmd1aW5fY2hpbnN0cmFwX21hc3MpCmBgYApXZSBub3cgaGF2ZSB0d28gZGF0YWZyYW1lcywgY29udGFpbmluZyBkYXRhIG9uIGRpZmZlcmVudCBzcGVjaWVzLCBhbmQgd2l0aCBvbmx5IGEgc3Vic2V0IG9mIHRoZSBkYXRhIGluIG9uZSB0aGF0IGlzIGNvbnRhaW5lZCBpbiB0aGUgb3RoZXIgKHRoZSBwZXRhbCB3aWR0aHMgYW5kIHNwZWNpZXMpLiBCdXQgaWYgcGV0YWwgd2lkdGggYW5kIHNwZWNpZXMgaXMgd2hhdCB3ZSB3YW50IHRvIHBsb3QsIHRoaXMgaXNuJ3QgYSBwcm9ibGVtIGZvciBnZ3Bsb3Q6CmBgYHtyfQpnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KGRhdGEgPSBwZW5ndWluc19ub25jaGluc3RyYXAsIGFlcyh4ID0gc3BlY2llcywgeSA9IGJvZHlfbWFzc19nLCBjb2xvciA9IHNwZWNpZXMpKSArCiAgZ2VvbV9ib3hwbG90KGRhdGEgPSBwZW5ndWluX2NoaW5zdHJhcF9tYXNzLCBhZXMoeCA9IHNwZWNpZXMsIHkgPSBib2R5X21hc3NfZywgY29sb3IgPSBzcGVjaWVzKSkKYGBgCgojIyBmYWNldHMKCkFub3RoZXIgZ3JlYXQgdG9vbCBnZ3Bsb3QgcHJvdmlkZXMgaXMgZmFjZXRpbmcuIFRoaXMgYWxsb3dzIHlvdSB0byBzZXBhcmF0ZSBkYXRhIGludG8gc3VicGxvdHMgYmFzZWQgb24gYSBjb2x1bW4gKG9yIG11bHRpcGxlIGNvbHVtbnMpOgpgYGB7cn0KYmFzaWNfcGVuZ3Vpbl9wbG90ICsKICBmYWNldF93cmFwKCB+IHNwZWNpZXMpCmBgYApOb3RpY2UgdGhhdCB0aGUgeC1heGVzIGFyZSBjb25zaXN0ZW50IGFtb25nIHRoZXNlIHBsb3RzLgoKIyMgTG90cyBvZiBhZGRpdGlvbmFsIHBhY2thZ2VzIQoKQmVjYXVzZSBnZ3Bsb3QgaXMgc28gcG9wdWxhciwgdGhlcmUncyBiZWVuIGEgdG9uIG9mIGFkZGl0aW9uYWwgcGFja2FnZXMgd3JpdHRlbiB0aGF0IGJ1aWxkIG9uIHRvcCBvZiBpdC4gSGVyZSBhcmUgdHdvIGV4YW1wbGVzLgoKIyMjIGdnYW5pbWF0ZQoKQWRkIGFuaW1hdGlvbnMgdG8gcGxvdHMKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cyA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCdnaWZza2knKQppbnN0YWxsLnBhY2thZ2VzKCdnZ2FuaW1hdGUnKQpsaWJyYXJ5KGdnYW5pbWF0ZSkKYW5pbWF0ZWRfcGVnbnVpbl9wbG90IDwtIGJhc2ljX3Blbmd1aW5fcGxvdCArIHRyYW5zaXRpb25fc3RhdGVzKHNwZWNpZXMpCiMgc2F2ZSBwbG90IHdpdGggMyBmcmFtZXMsIHBsYXkgYXQgMSBmcmFtZSBwZXIgc2Vjb25kCmFuaW1fc2F2ZSgiYW5pbWF0ZWRfcGVuZ3Vpbl9wbG90LmdpZiIsIGFuaW1hdGVkX3Blbmd1aW5fcGxvdCwgbmZyYW1lcyA9IDQsIGZwcyA9IDEpCiMgcmVuZGVyIHBsb3QgdXNpbmcgIVtdKHBsb3RfcGF0aCkgb3V0c2lkZSBvZiBjb2RlIGNodW5rCmBgYAo=